package de.hsowl.tod4a.client;

import java.util.List;

import de.hsowl.tod4a.client.query.Comparator;
import de.hsowl.tod4a.global.exception.ObjectDatabaseException;
import de.hsowl.tod4a.global.listener.DataChangedListener;

/**
 * API to use the Database and all its features. This class is the only
 * interface between zhe user and database.
 * 
 * @author Sebastian Witt
 * 
 */
public abstract interface EntityManager {

	/**
	 * Stores Objects or updates them if they are already stored. Usage: if you
	 * want to store/update several object than pass then in an array. <br>
	 * e.g.: for(int i=0; i<10; i++) {<br>
	 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
	 * entityManager.store(new Customer());<br>
	 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
	 * is not high performance. Try to pass an array like list.toArray() or
	 * start a transaction with<br>
	 * entityManager.begin();<br>
	 * *your operations*<br>
	 * entityManager.commit();
	 * 
	 * 
	 * @Warning Static fields will NOT stored!
	 * 
	 * @param objects
	 *            to store
	 */
	public void store(Object... objects) throws ObjectDatabaseException;

	/**
	 * Stores Objects with custom names or updates them if they are already
	 * stored. Usage: if you want to store/update several object than pass then
	 * in an array. <br>
	 * e.g.: for(int i=0; i<10; i++) {<br>
	 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
	 * entityManager.store(new Customer());<br>
	 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
	 * is not high performance. Try to pass an array like list.toArray() or
	 * start a transaction with<br>
	 * entityManager.begin();<br>
	 * *your operations*<br>
	 * entityManager.commit();
	 * 
	 * @param objects
	 *            objects to store
	 * @param names
	 *            objectsnames
	 * @throws ObjectDatabaseException
	 */
	public void store(Object[] objects, String[] names) throws ObjectDatabaseException;

	/**
	 * Stores an Object with custom name or updates it if it is already stored.
	 * Usage: if you want to store/update several object than pass then in an
	 * array. <br>
	 * e.g.: for(int i=0; i<10; i++) {<br>
	 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
	 * entityManager.store(new Customer());<br>
	 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
	 * is not high performance. Try to pass an array like list.toArray() or
	 * start a transaction with<br>
	 * entityManager.begin();<br>
	 * *your operations*<br>
	 * entityManager.commit();
	 * 
	 * 
	 * @Warning Static fields will NOT stored!
	 * 
	 * @param objects
	 *            to store
	 * @param name
	 *            name of the object
	 */
	public void store(Object object, String name) throws ObjectDatabaseException;

	/**
	 * Removes objects and children by their IDs.
	 * 
	 * @param objects
	 *            to remove
	 */
	public void remove(Object... objects) throws ObjectDatabaseException;

	/**
	 * Removes objects by their IDs
	 * 
	 * @param objects
	 * @param children
	 *            - false->only one object will reoved; true->all objects relted
	 *            to this pbject will removed
	 * @return true->no error; false->an error occurred; null->saved in
	 *         transaction, commit later
	 */
	public void remove(boolean children, Object... objects) throws ObjectDatabaseException;

	/**
	 * Queries for all objects that matches the in T provided class and there
	 * compare() returns true.
	 * 
	 * @param comperator
	 * @return
	 */
	public <T> List<T> get(Comparator<T> comperator);

	/**
	 * Queries for all objects and 'depth' reference layer that matches the in T
	 * provided class and there compare() returns true.
	 * 
	 * @param comperator
	 * @param depth
	 *            - 0->only parent object
	 * @return
	 */
	public <T> List<T> get(Comparator<T> comperator, int depth);

	/**
	 * Queries for Objects by their Class.
	 * 
	 * @param clazz
	 * @return
	 */
	public <T> List<T> getByClass(Class<?> clazz);

	/**
	 * Queries for Objects by their Class, but only for 'depth' layers
	 * 
	 * @param clazz - class of the objects
	 * @param depth
	 *            - 0->only parent object
	 * @return
	 */
	public <T> List<T> getByClass(Class<?> clazz, int depth);

	/**
	 * Queries for Objects by an OQL Statement. Only objects with the same class
	 * as 'clazz' will returned.
	 * 
	 * @param query
	 * @param clazz - class of the objects
	 * @return
	 */
	public <T> List<T> getByOQL(String query, Class<?> clazz);

	/**
	 * Queries for Objects by an OQL Statement. Every Object that matches the
	 * statement will returned.
	 * 
	 * @param query
	 * @return
	 */
	public List<Object> getByOQL(String query);

	/**
	 * Queries for Objects by their names.<br>
	 * If no custom name was given the name is the result of toString().
	 * 
	 * @param name
	 * @return
	 */
	public <T> List<T> getByName(String name, Class<?> clazz);

	/**
	 * Queries for Objects by their names.<br>
	 * Only objects of the specified class are returned.<br>
	 * If no custom name was given the name is the result of toString().<br>
	 * This method will return various types of objects.
	 * 
	 * @param name - the name of an object
	 * @param clazz
	 * @return
	 */
	public List<Object> getByName(String name);

	/**
	 * Queries for Objects and 'depth' layers by their names.<br>
	 * Only objects of the specified class are returned.<br>
	 * If no custom name was given the name is the result of toString().<br>
	 * This method will return various types of objects.
	 * 
	 * @param name - the name of an object
	 * @param depth - 0->only the parent will returned
	 * @return
	 */
	public List<Object> getByName(String name, int depth);

	/**
	 * Queries for Objects by their names.<br>
	 * If no custom name was given the name is the result of toString().
	 * 
	 * @param name - the name of an object
	 * @param clazz - class of the sepcific objects
	 * @param depth - 0->only the parent will returned
	 * @return
	 */
	public <T> List<T> getByName(String name, Class<?> clazz, int depth);

	/**
	 * Adds or updates an DB user.
	 * 
	 * @param username
	 * @param password
	 */
	public void addUser(String username, String password) throws ObjectDatabaseException;

	/**
	 * Deltes an existing user by its name.
	 * 
	 * @param username
	 */
	public void removeUser(String username) throws ObjectDatabaseException;

	/**
	 * Closes an existing server. Only needs to be called when a server has
	 * started.
	 */
	public void close();

	/**
	 * Starts a new transaction.<br>
	 * E.g.:<br>
	 * em.begin();<br>
	 * em.store();<br>
	 * em.remove();<br>
	 * em.store();<br>
	 * em.commit();
	 */
	public void begin();

	/**
	 * Commits a transaction
	 * 
	 * @return
	 * @throws Exception
	 */
	public boolean commit() throws ObjectDatabaseException;

	/**
	 * Starts a database rollback. This will restore the database before the
	 * last transaction
	 */
	public void rollback() throws ObjectDatabaseException;

	/**
	 * Starts a database rollback. This will restore the database before the
	 * last X transactions
	 * 
	 * @param num
	 *            - the amount of transactions to roll back
	 */
	public void rollback(int num) throws ObjectDatabaseException;

	/**
	 * Adds a listener to the database. Whenever a value in a class with
	 * listener is changed the 'onDataChanged' method is triggered.<br>
	 * E.g.: <br>
	 * EntityManager em = EntityManagerFactory.embeddedSession(path);<br>
	 * em.addDataChangedListener(new DataChangedListener(Test.class) { <br>
	 * public void onDataChanged(String clazz) { <br>
	 * do somthing...<br>
	 * });<br>
	 * Creates a listener for the test class. <br>
	 * Or: <br>
	 * em.addDataChangedListener(new DataChangedListener(Test.class,Test2.class)
	 * { <br>
	 * public void onDataChanged(String clazz) { <br>
	 * do somthing...<br>
	 * });<br>
	 * Creates a listener for the test and test2 class.
	 * 
	 * @param listener
	 */
	public void addDataChangedListener(DataChangedListener listener);

}
